/*
 * Copyright (C) 2010 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.action;

import ch.trackedbean.binding.action.MenuPath.MenuPathSegment;

/**
 * Helper object for building an {@link MenuPath} path.<br>
 * You may create a 'parent' path builder and clone it to build sub paths.
 * 
 * @author M. Hautle
 */
public class MenuPathBuilder implements Cloneable {
    /** The default order value. */
    public static final int DEFAULT_ORDER = Integer.MAX_VALUE / 2;

    /** The biggest order value - so the one to place an entry at the end. */
    public static final int LAST_ORDER = Integer.MAX_VALUE;

    /** The lowest order value - so the one to place an entry at the beginning. */
    public static final int FIRST_ORDER = 0;

    /** The path segments. May contain 'unused' entries - all those with an index greater than {@link #stackPointer}. */
    private Segment[] menus = { new Segment() };

    /** The stackpointer of {@link #menus}. */
    private int stackPointer = 0;

    /**
     * Adds the given submenu to the path.
     * 
     * @param subMenu The name of the sub menu
     * @return The path builder
     */
    public MenuPathBuilder add(String subMenu) {
        return add(subMenu, DEFAULT_ORDER);
    }

    /**
     * Sets the group of the current path element.
     * 
     * @param group The group name
     * @return The path builder
     */
    public MenuPathBuilder group(String group) {
        return group(group, DEFAULT_ORDER);
    }

    /**
     * Adds the given submenu to the path.
     * 
     * @param subMenu The name of the sub menu
     * @param subMenuOrder The order of the sub menu (lower values come first)
     * @return The path builder
     */
    public MenuPathBuilder add(String subMenu, int subMenuOrder) {
        menus[stackPointer].subMenu = subMenu;
        menus[stackPointer].subMenuOrder = subMenuOrder;
        addNext();
        return this;
    }

    /**
     * Sets the group of the current path element.
     * 
     * @param group The group name
     * @param groupOrder The order of the group (lower values come first)
     * @return The path builder
     */
    public MenuPathBuilder group(String group, int groupOrder) {
        menus[stackPointer].group = group;
        menus[stackPointer].groupOrder = groupOrder;
        return this;
    }

    /**
     * Resets this path.
     * 
     * @return The path builder
     */
    public MenuPathBuilder reset() {
        stackPointer = 0;
        menus[0].reset();
        return this;
    }

    /**
     * Returns the previously built path.
     * 
     * @return The path
     */
    public MenuPath get() {
        final MenuPathSegment[] res = new MenuPathSegment[stackPointer];
        for (int i = 0; i < res.length; i++)
            res[i] = menus[i].toSegment();
        return new MenuPath(res, menus[stackPointer].group, menus[stackPointer].groupOrder);
    }

    /**
     * Adds an empty entry to the path.
     */
    private void addNext() {
        if (++stackPointer < menus.length) {
            menus[stackPointer].reset();
            return;
        }
        final Segment[] old = menus;
        menus = new Segment[menus.length + 1];
        System.arraycopy(old, 0, menus, 0, old.length);
        menus[stackPointer] = new Segment();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MenuPathBuilder clone() {
        try {
            final MenuPathBuilder c = (MenuPathBuilder) super.clone();
            c.menus = new Segment[stackPointer + 1];
            for (int i = 0; i < c.menus.length; i++)
                c.menus[i] = menus[i].clone();
            return c;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Mutable version of {@link MenuPathSegment}.
     * 
     * @author M. Hautle
     */
    private static class Segment implements Cloneable {
        /** The name of the next sub menu or null if there is no sub menu. */
        String subMenu;

        /** The order value of {@link #subMenu}. */
        int subMenuOrder;

        /** The name of the group to which this entry belongs or null for the default group. */
        String group;

        /** The oder value of {@link #group}. */
        int groupOrder;

        /**
         * Resets the entry content.
         * 
         * @return This entry
         */
        Segment reset() {
            subMenu = null;
            group = null;
            subMenuOrder = DEFAULT_ORDER;
            groupOrder = DEFAULT_ORDER;
            return this;
        }

        /**
         * Creates a {@link MenuPathSegment} for this entry.
         * 
         * @return The segment
         */
        MenuPathSegment toSegment() {
            return new MenuPathSegment(group, groupOrder, subMenu, subMenuOrder);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Segment clone() {
            try {
                return (Segment) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
